package task;



import edu.buaa.act.helper.Arguments;
import edu.buaa.act.helper.CsvGPSFileReader;
import edu.buaa.act.helper.RawDataCleaning;
import edu.buaa.act.helper.Utils;
import edu.buaa.act.model.GPSPoint;
import edu.buaa.act.model.tsc.ValidationTask;

import java.io.*;
import java.text.ParseException;
import java.util.*;

import static edu.buaa.act.helper.Arguments.Values.*;
import static edu.buaa.act.helper.CsvGPSFileReader.*;
import static edu.buaa.act.model.tsc.ValidationTask.globalConf;

/**
 * Created by song on 17-7-4.
 *
 * Rewrite of Normal TSC method
 */
public class EvalUtilityByTSC
{
    public static void main(String[] args)
    {
        List<ValidationTask> completeTasks = new ArrayList<>();
        try
        {
            if(args.length<9){
                displayHelpMsg();
                generateExampleParamCSV(new File("param.csv"));
                return;
            }

            List<ValidationTask> tasks = ValidationTask.parseArgs(args);

            List<List<GPSPoint>> data = readAndClean();

            for(ValidationTask task : tasks)
            {
                task.run(data);
                completeTasks.add(task);
            }

        } catch (IOException | ParseException | InterruptedException | RuntimeException e) {
            Utils.getDebugLogger().error("program exit with error: ", e);
        } finally
        {
            Utils.getDebugLogger().info("RESULT TABLE:\n{}",printResultTable(completeTasks));
        }
    }

    private static void generateExampleParamCSV(File file) throws IOException
    {
        BufferedWriter w = new BufferedWriter(new FileWriter(file));
        StringBuilder sb = new StringBuilder();
        sb.append(
                "# This file is read by the TSC program and execute line by line (line start with # are ignored).\n" +
                        "# Each line represent a task which has 14 configurable params.\n" +
                        "# Params are explained below (in order). For detailed description of all params (including global params which apply to all tasks), see edu.buaa.act.helper.Arguments\n#\n");
        for(Arguments.Values v : taskInputParams())
        {
            sb.append("# ").append(v).append("\n")
                    .append("#   Chinese description: ").append(v.toString(false)).append("\n")
                    .append("#   English description: ").append(v.toString(true)).append("\n")
                    .append("#   Example Value:       ").append(v.example()).append('\n')
                    .append("#   More notes:          ").append(v.more()).append("\n\n");
        }

        sb.append("#\n# Below is an example of task. When using this file, You should comment it and append your tasks below\n");
        sb.append("# IMPORTANT: params must in the SAME ORDER as above (from left/top to right/down).\n");
        for(Arguments.Values v : taskInputParams())
        {
            sb.append(v.example()).append(", ");
        }
        w.write(sb.substring(0, sb.length()-2)+"\n");
        w.close();
    }

    private static List<List<GPSPoint>> readAndClean() throws IOException, ParseException {
        String fileName = globalConf.get(Raw_Traj_Data_File);
        File data = new File(fileName);
        if(data.exists() && data.isFile()){
            List<GPSPoint> raw = CsvGPSFileReader.readLargeFile(data, " ", CAR_ID, LATITUDE, LONGITUDE, TIMESTAMP);

            double[] geoBound = globalConf.get(Filter_Geo_Bound);
            List<GPSPoint> data0 = RawDataCleaning.filterByGeoBound(raw, geoBound[0], geoBound[1], geoBound[2], geoBound[3]);



            RawDataCleaning.setCarID(data0);

            List<List<GPSPoint>> data1 = RawDataCleaning.separateTrajByCarID(data0);

            RawDataCleaning.assertTimeInc(data1);
            RawDataCleaning.assertSpeedLimit(data1);

            return data1;

        }else{
            throw new FileNotFoundException(fileName);
        }
    }


    private static String printResultTable(List<ValidationTask> tasks)
    {
        Arguments.Values[] outputVars = new Arguments.Values[]{
                Train_Data_History_Road_Count,
                Train_Data_History_Traj_Count,
                Train_Data_History_Total_Length,
                Train_Data_History_Total_Time,
                Train_Data_Recent_Road_Count,
                Train_Data_Recent_Traj_Count,
                Train_Data_Recent_Total_Length,
                Train_Data_Recent_Total_Time,
                Train_Data_Direct_Length,
                Train_Data_MM_IN_OUT_RATIO,
                Test_Data_Total_Length,
                Test_Data_Direct_Length,
                Test_Data_Total_Time,
                Test_Data_Traj_Count,
                Test_Data_MM_IN_OUT_RATIO,
                Train_Traj_AVG_Length,
                Train_Traj_AVG_Time,
                Test_Traj_AVG_Length,
                Test_Traj_AVG_Time,
                Mean_Absolute_Error,
                Mean_Relative_Error,
                MAE_Per_Kilometer,
                //INPUT
                // task specific config
                Test_With_Anonymous_Data,
                Separate_Real_With_Time_Window,
                Anonymous_Separately,
                Test_Data_Count,
                Test_Data_Filter_Start_Time,
                Test_Data_Filter_End_Time,
                Test_Data_L2S_Time_Interval,
                Anonymity_Level_K,
                Anonymity_L2S_Time_Window_Size,
                Anonymity_L2S_Time_Window_Start_Threshold,
                Anonymity_L2S_Time_Window_End_Threshold,
                Anonymity_Interpolation_Time_Interval,
                MapMatching_GPS_Accuracy,
                Filter_Start_Time,
                Filter_End_Time,
                // global config
                Parallel_Count,
                Raw_Traj_Data_File,
                MapMatching_Map_Data_Path,
                Filter_Geo_Bound,
                Anonymity_NWA_EXE_Path,
        };
        StringBuilder sb = new StringBuilder();
        sb.append(globalConf.tableHeader(outputVars)).append('\n');
        for(ValidationTask task : tasks){
            sb.append(task.tableRow(outputVars)).append('\n');
        }
        return sb.toString();
    }



    private static List<Arguments.Values> taskInputParams()
    {
        return Arrays.asList(
                Test_With_Anonymous_Data,
                Anonymous_Separately,
                Separate_Real_With_Time_Window,
                MapMatching_GPS_Accuracy,

                Filter_Start_Time,
                Filter_End_Time,

                Test_Data_Count,
                Test_Data_Filter_Start_Time,
                Test_Data_Filter_End_Time,
                Test_Data_L2S_Time_Interval,

                Anonymity_Level_K,
                Anonymity_Interpolation_Time_Interval,

                Anonymity_L2S_Time_Window_Size,
                Anonymity_L2S_Time_Window_Start_Threshold,
                Anonymity_L2S_Time_Window_End_Threshold

        );
    }


    private static void displayHelpMsg()
    {
        System.out.println("\n" +
                "#######################################################################\n" +
                "#  HELP INFORMATION FOR Trajectory Simple Concatenation (TSC) Program:\n" +
                "# ---------------------------------------------------------------------\n" +
                "#\n" +
                "# This program runs with 20 params, 5 global, 15 task specific.\n" +
                "# Global params are past through command line when starting this program, which apply for all tasks.\n" +
                "# Task params are write in the 'param.csv', in which tasks are batch processed.\n" +
                "#######################################################################\n");
    }


}
